/*
 *  Copyright 1999-2019 Seata.io Group.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package io.kubesphere.devops.core;

import com.google.common.eventbus.EventBus;
import io.kubesphere.devops.common.PhaseTwoTransactionHandler;
import io.kubesphere.devops.model.MergeAction;
import io.kubesphere.devops.model.PartitionAction;
import io.kubesphere.devops.service.MergeCommitEvent;
import io.kubesphere.devops.service.MergeLogService;
import io.kubesphere.devops.service.MergeRevertEvent;
import io.kubesphere.devops.service.SimpleLogListener;
import io.seata.common.exception.ShouldNeverHappenException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class DataSourceManager implements PhaseTwoTransactionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceManager.class);

    private final AsyncWorker asyncWorker = new AsyncWorker(this);

    private final DataSource dynamicDataSource;

    public DataSourceManager(DataSource dynamicDataSource) {
        this.dynamicDataSource = dynamicDataSource;
    }

    public BranchStatus branchCommit(String xid, String branchId, String resourceId) throws RuntimeException {
        return asyncWorker.branchCommit(xid, branchId, resourceId);
    }

    public BranchStatus branchRollback(String xid, String branchId) throws RuntimeException {
        DataSourceProxy dataSourceProxy = null;
        if (dynamicDataSource instanceof DynamicRoutingDataSource) {
            //获取当前待操作分库数据源
            DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dynamicDataSource;
            dataSourceProxy = (DataSourceProxy) dynamicRoutingDataSource.determineTargetDataSource();
        }

        if (dataSourceProxy == null) {
            throw new ShouldNeverHappenException();
        }
        UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType()).undo(dataSourceProxy, xid, branchId);
        return BranchStatus.PhaseTwo_Rollbacked;

    }

    @Override
    public void commit(MergeAction action) {
        List<PartitionAction> partitionActions = action.getPartitionActionList().stream()
                .sorted(Comparator.<PartitionAction, Timestamp>comparing(b -> b.getUserMergingTimestamp()).reversed()).collect(Collectors.toList());
        //处理不同分支数据
        partitionActions.forEach(partitionAction -> {
                    DynamicDataSourceContextHolder.setDataSourceKey(partitionAction.getDatasourceId());
                    MergeCommitEvent mergeCommitEvent = new MergeCommitEvent(partitionAction.getUserMergingId(), partitionAction.getUserMergingDetailId());
                    MergingEventCenter.post(mergeCommitEvent);
                }

        );
    }

    @Override
    public void rollback(MergeAction action) {
        List<PartitionAction> partitionActions = action.getPartitionActionList().stream()
                .sorted(Comparator.<PartitionAction, Timestamp>comparing(b -> b.getUserMergingTimestamp()).reversed()).collect(Collectors.toList());
        //处理不同分支数据
        partitionActions.forEach(partitionAction -> {
                    DynamicDataSourceContextHolder.setDataSourceKey(partitionAction.getDatasourceId());
                    BranchStatus status = this.branchRollback(partitionAction.getUserMergingId(), partitionAction.getUserMergingDetailId());
                    MergeRevertEvent mergeRevertEvent = new MergeRevertEvent(partitionAction.getUserMergingId(), partitionAction.getUserMergingDetailId());
                    MergingEventCenter.post(mergeRevertEvent);
                }

        );
    }

    public DataSource getDynamicDataSource() {
        return this.dynamicDataSource;
    }
}
